//! 贡献关键字管理器
//!
//! 对应 `ai-server` 的 `services/contribution_keyword_manager.py`。

use std::any::Any;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};

use crate::core::beans::bean::{Bean, BeanManager, IDestructible};
use crate::core::beans::constants::expansion_feature_names;
use crate::core::manifest::{ContributeDefinition, ContributionPointDefinition};
use crate::core::services::context_manager::ContextManager;
use crate::core::services::event_manager::{expansion_lifecycle_event_names, EventManager};
use crate::core::services::manifest_manager::ManifestManager;
use crate::core::services::protocols::*;

fn fail(message: &str) {
    // 在开发环境中 panic，在生产环境中可以考虑只打印日志
    panic!("{}", message);
}

fn merge_contributes(
    a: &Option<HashMap<String, Vec<ContributeDefinition>>>,
    b: &Option<HashMap<String, Vec<ContributeDefinition>>>,
) -> Option<HashMap<String, Vec<ContributeDefinition>>> {
    let mut result: HashMap<String, Vec<ContributeDefinition>> = HashMap::new();
    if let Some(a_map) = a {
        for (k, v) in a_map {
            result.entry(k.clone()).or_default().extend(v.iter().cloned());
        }
    }
    if let Some(b_map) = b {
        for (k, v) in b_map {
            result.entry(k.clone()).or_default().extend(v.iter().cloned());
        }
    }
    if result.is_empty() { None } else { Some(result) }
}

fn merge_contribution_points(
    a: &Option<HashMap<String, Vec<ContributionPointDefinition>>>,
    b: &Option<HashMap<String, Vec<ContributionPointDefinition>>>,
) -> Option<HashMap<String, Vec<ContributionPointDefinition>>> {
    let mut result: HashMap<String, Vec<ContributionPointDefinition>> = HashMap::new();
    if let Some(a_map) = a {
        for (k, v) in a_map {
            result.entry(k.clone()).or_default().extend(v.iter().cloned());
        }
    }
    if let Some(b_map) = b {
        for (k, v) in b_map {
            result.entry(k.clone()).or_default().extend(v.iter().cloned());
        }
    }
    if result.is_empty() { None } else { Some(result) }
}


/// 贡献关键字管理器
pub struct ContributionKeywordManager {
    keyword_impls: Arc<Mutex<HashMap<String, Arc<dyn IContributionKeyword>>>>,
    disposers: Mutex<Vec<Box<dyn FnOnce() + Send + Sync>>>,
    manifest_manager: Arc<ManifestManager>,
    context_manager: Arc<ContextManager>,
    event_manager: Arc<EventManager>,
}

impl IDestructible for ContributionKeywordManager {
    fn destructor(&self) {
        for disposer in self.disposers.lock().unwrap().drain(..) {
            disposer();
        }
        // Also call destructor on all keyword impls
        let impls = self.keyword_impls.lock().unwrap();
        for impl_bean in impls.values() {
            impl_bean.destructor();
        }
        println!("ContributionKeywordManager destroyed.");
    }
}

impl Bean for ContributionKeywordManager {
    fn new(bean_manager: &Arc<BeanManager>) -> Arc<Self> {
        let manager = Arc::new(Self {
            keyword_impls: Arc::new(Mutex::new(HashMap::new())),
            disposers: Mutex::new(Vec::new()),
            manifest_manager: bean_manager.get_bean(expansion_feature_names::MANIFEST_MANAGER).unwrap(),
            context_manager: bean_manager.get_bean(expansion_feature_names::CONTEXT_MANAGER).unwrap(),
            event_manager: bean_manager.get_bean(expansion_feature_names::EVENT_MANAGER).unwrap(),
        });

        manager.clone().subscribe_to_events();
        manager
    }
}

impl ContributionKeywordManager {
    fn subscribe_to_events(self: Arc<Self>) {
        let mut disposers = self.disposers.lock().unwrap();
        
        let s = self.clone();
        disposers.push(self.event_manager.on(expansion_lifecycle_event_names::BEFORE_EXTENSION_ACTIVATE, Box::new(move |p| s.before_extension_activate_handler(p.downcast_ref::<String>().unwrap()))));
        
        let s = self.clone();
        disposers.push(self.event_manager.on(expansion_lifecycle_event_names::ON_EXTENSION_DEACTIVATED, Box::new(move |p| s.on_extension_deactivated_handler(p.downcast_ref::<String>().unwrap()))));
        
        type AddedPayload<T> = (String, HashMap<String, Vec<T>>);

        let s = self.clone();
        disposers.push(self.event_manager.on(expansion_lifecycle_event_names::ON_DYNAMIC_CONTRIBUTES_ADDED, Box::new(move |p| {
            if let Some((name, contributes)) = p.downcast_ref::<AddedPayload<ContributeDefinition>>() {
                s.add_contributes(name, contributes.clone());
            }
        })));

        let s = self.clone();
         disposers.push(self.event_manager.on(expansion_lifecycle_event_names::ON_DYNAMIC_CONTRIBUTION_POINTS_ADDED, Box::new(move |p| {
            if let Some((name, points)) = p.downcast_ref::<AddedPayload<ContributionPointDefinition>>() {
                s.add_contribution_points(name, points.clone());
            }
        })));

        // TODO: Add handlers for remove events
    }

    fn before_extension_activate_handler(&self, name: &str) {
        if let Some(manifest) = self.manifest_manager.get_manifest(name) {
            if let Some(cps) = merge_contribution_points(&manifest.contribution_points, &manifest.dynamic_contribution_points) {
                self.add_contribution_points(name, cps);
            }
            if let Some(cs) = merge_contributes(&manifest.contributes, &manifest.dynamic_contributes) {
                self.add_contributes(name, cs);
            }
        }
    }

    fn on_extension_deactivated_handler(&self, name: &str) {
        if let Some(manifest) = self.manifest_manager.get_manifest(name) {
            if let Some(cs) = &manifest.contributes {
                self.remove_contributes(name, cs.clone());
            }
            if let Some(cps) = &manifest.contribution_points {
                self.remove_contribution_points(name, cps.clone());
            }
        }
    }

    pub fn add_contribution_points(&self, contributor: &str, points: HashMap<String, Vec<ContributionPointDefinition>>) {
        for (keyword, definitions) in points {
            if let Some(impl_bean) = self.keyword_impls.lock().unwrap().get(&keyword) {
                let ctx = self.context_manager.get_request_contribution_context(contributor, &keyword);
                impl_bean.request_contribution_point(ctx, &definitions);
            }
        }
    }

    pub fn remove_contribution_points(&self, contributor: &str, points: HashMap<String, Vec<ContributionPointDefinition>>) {
        for (keyword, definitions) in points {
            if let Some(impl_bean) = self.keyword_impls.lock().unwrap().get(&keyword) {
                let ctx = self.context_manager.get_request_contribution_context(contributor, &keyword);
                impl_bean.request_revoke_contribution_point(ctx, &definitions);
            }
        }
    }

    pub fn add_contributes(&self, contributor: &str, contributes: HashMap<String, Vec<ContributeDefinition>>) {
        for (keyword, definitions) in contributes {
            if let Some(impl_bean) = self.keyword_impls.lock().unwrap().get(&keyword) {
                let ctx = self.context_manager.get_request_contribution_context(contributor, &keyword);
                impl_bean.request_contribution(ctx, &definitions);
            }
        }
    }

    pub fn remove_contributes(&self, contributor: &str, contributes: HashMap<String, Vec<ContributeDefinition>>) {
        for (keyword, definitions) in contributes {
            if let Some(impl_bean) = self.keyword_impls.lock().unwrap().get(&keyword) {
                let ctx = self.context_manager.get_request_contribution_context(contributor, &keyword);
                impl_bean.request_revoke_contribution(ctx, &definitions);
            }
        }
    }

    pub fn implement_contribution_keyword(&self, name: &str, impl_bean: Arc<dyn IContributionKeyword>) -> Disposer {
        let mut impls = self.keyword_impls.lock().unwrap();
        if impls.contains_key(name) {
            fail(&format!("keyword '{}' has already been implemented", name));
        }

        impl_bean.init(ContributionKeywordInitOptions {});
        impls.insert(name.to_string(), impl_bean.clone());

        let name_clone = name.to_string();
        let keyword_impls_weak = Arc::downgrade(&self.keyword_impls);

        Box::new(move || {
            if let Some(keyword_impls_arc) = keyword_impls_weak.upgrade() {
                let mut impls = keyword_impls_arc.lock().unwrap();
                if let Some(removed_impl) = impls.remove(&name_clone) {
                    removed_impl.destructor();
                }
            }
        })
    }

    pub fn get_contribution_keyword_impl(&self, keyword: &str) -> Option<Arc<dyn IContributionKeyword>> {
        self.keyword_impls.lock().unwrap().get(keyword).cloned()
    }

    pub fn get_contribution_keyword_context(&self, keyword: &str, contributor: &str) -> Option<Arc<dyn Any + Send + Sync>> {
        self.get_contribution_keyword_impl(keyword)
            .and_then(|impl_bean| impl_bean.get_context(contributor))
    }
}
